#ifndef _QGCM_DIAGNOSTICS_
#define _QGCM_DIAGNOSTICS_
class psystem;
class ssystem;

#include <vector>
#include <mpParser.h>
#include "datatypes.h"
#include "forward_classes.h"
#include "qgcmio.h"
#include "diagfuncs.h"

using namespace mup;

class model_diagnostics_block;
void assigndiagfuncs(ParserX & P, bool);
/* -------------------------------------------------------------------------

   class model_diagnostics_block :

   -------------------------------------------------------------------------- */
class model_diagnostics_block {
	// - - - - - - - - - - - - - - - - - - - - - - - - -
	/* friends */
	friend class model_class;
	friend class psystem;
	friend class ssystem;
	friend class compiler;
	friend class debugger;
	// - - - - - - - - - - - - - - - - - - - - - - - - -
	/* typedefs */
	enum blockattribute { function = 0, repeatblock = 1, endblock = 2, startblock = 3, statusblock = 4,
		scriptblock = 5 } blocktype;
	struct diagvariable {
		string name;
		char_type dtype; // what type of variable is this supposed to be
		Value * data; // pointer to storage location that holds the IValue 
		bool isstatic;
		private:
		diagvariable(); // disabled 
		//diagvariable & operator=(const diagvariable &);
		//diagvariable(const diagvariable &);
		public:
		diagvariable(const diagvariable &);
		diagvariable & operator=(const diagvariable &);
		diagvariable(const string & name, Value * data, char_type ct, bool isstatic);
		void assert_type_correctness() const; // throws ParserError if types do not agree on variables 
		~diagvariable();
	};
	struct instruction {

		enum inst { inst_mu = 0, inst_jmp = 1, inst_jmpnif = 2, 
			inst_exit = 3, inst_jmpif = 4, inst_return = 5, inst_trap = 6 };

		instruction();
		inst id;
		int jmpinstr; 
		bool runonce;

		struct {
			bool breakpointset;
			int namelist;    // used for debugging purposes, pointer to the namelist in model
			int slinenumber; // linenumber associated with this instruction in file above ...
			int elinenumber; // linenumber associated with this instruction in file above ...
		} debug_attributes;

		ParserX * parser; // if one is allocated
		const char * getname() const;
		~instruction();

		private:
		instruction(const instruction &);  // disabled
		instruction operator=(const instruction &); // disabled 
	}; 
	// - - - - - - - - - - - - - - - - - - - - - - - - -
	model_class * mo; // pointer to model_class that owns this diag block

	struct {
		int numargs; 
		unsigned int depth; // how deeply nested in the function are we
		char_type ret_type;
		vector< vector<Value> * > saved_variables; // necessary for function nesting ..
	} function_attributes;

	struct {
		string name; 
		bool allowimplicit; // global implicit variable creation flag
		unsigned int timesrunned; // how many times has this block been run
	} attributes;

	struct {
		int nid; // namelist id
		int slinenumber;
		int elinenumber;
	} debug_attributes;

	/* private flags and objects */

	vector< instruction * > instructions;    // list of instructions  
	vector< diagvariable > variables;        // list of current diag variables relevant to the stack
	stack< vector<unsigned int> > variable_scope_resolution_list;


	public:
	vector<char_type> GetCallStackTypes() const;
	char_type GetReturnType() const;
	size_t getvariablessize() const;
	bool isvariabledefined(const string & name) const;
	Variable addvariable(const string & name, const Value & init, char_type, bool isstatic);
	void assignarglist(const ptr_val_type * args, const int size);
	int getfuncnumargs() const;
	const string & GetVariableName(int i) const;
	const string getBlockAsText() const; 
	// - - - - - - - - - - - - - - - - - - - - - - - - -
	/* disabled constructors (to prevent errors): */
	private:
	model_diagnostics_block();
	model_diagnostics_block(const model_diagnostics_block &);
	public:
	// - - - - - - - - - - - - - - - - - - - - - - - - -
	/* public CI: */
	model_diagnostics_block(model_class & model);
	~model_diagnostics_block();
	model_class & getModel(); 
	const string & getfuncname() const;
	bool isblockfunction() const;
	bool isblockscript() const;
	bool isblockendscript() const;
	bool isblockstartscript() const;
	// - - - - - - - - - - - - - - - - - - - - - - - - -
	/* private CI interface : */
	private:
	bool runcode(Value &, bool & return_called, bool & debug); // called by runfunction and runblock
	bool executeinstruction(Value & rvalue, bool & return_called,
			size_t & cinstr, bool & debug); // called by runfunction and runblock
	void assert_type_correctness() const; 
	// - - - - - - - - - - - - - - - - - - - - - - - - -
	/* public CI run interface: */
	public:
	bool runscriptblock(bool debug=false);
	void runfunction(Value & ret, bool debug=false); // similar as runblock really except returns a number 
	// fails if block is not a func
	bool runrepeatblock(bool debug=false); // only runs the block if it is a repeatblock
	bool runendblock(bool debug = false);
	bool runstartblock(bool debug = false); 
	bool runstatusblock(bool debug= false); // should only be called with SIGINFO signal

	void setallowimplicit(bool);

	void pushscope();
	void popscope();

	const vector<instruction> & getinstructions() const;

};

// global call back for user_defined_functions
// ----------------------------------------------------------------------------------
// global call back class for user defined functions
class user_defined_function : public ICallback {
        string func_sig;
	model_diagnostics_block * diag_block; // owning block
	private:
	void CheckArgs(const ptr_val_type *, const int) const;
	void CheckRetType(const ptr_val_type & ret) const;
	public :
	user_defined_function(model_diagnostics_block *, char_type rtype, const vector<char_type> & argtypes);
	virtual void Eval(ptr_val_type & ret, const ptr_val_type * a_pArg, int a_iArgc);
	virtual const char_type * GetDesc() const;
        virtual const char_type * GetProtoType() const;
	virtual IToken * Clone() const;
        virtual bool IsAssignmentOP() const { return false; }
};

#endif
